
# Python imports
import logging

# Package imports
import pmutils

__all__ = [
    'Variant',
    'VariantValues',
    'new_variant_value',
    ]


class VariantError(KeyError):
    pass

class Variant(object):
    def __init__(self, name, values=None, complete=False):
        self.name = name
        self.values = values
        self.__complete = complete

    def reconcile(self, variant):
        assert self.name == variant.name

        # check that the variant values we have so far are
        # in the supplied variant if that supplied variant
        # is marked as complete.
        if variant.__complete:
            for v in self.values:
                if v not in variant.values:
                    logging.error(_("Variant Value set to an invalid option"))
                    raise VariantError()
        # Update our values with those past in.
        for v in variant.values:
            if v not in self.values:
                if self.__complete:
                    logging.error(_("Augmenting a variant list that is not changable"))
                    raise VariantError()
                self.values.append(v)


class VariantValue(object):
    def __init__(self, variant, value):
        assert isinstance(variant, Variant)
        self.variant = variant
        self.value = value

    def reconcile(self, variant_value):
        self.variant.reconcile(variant_value.variant)


def new_variant_value(name, values, value):
    assert value in values
    variant = Variant(name, values, False)
    return VariantValue(variant, value)


class VariantValues(list):
    """This class is essentially a list of VariantValue objects."""
    def __init__(self, initial=[]):
        assert isinstance(initial, list) or isinstance(initial, tuple)
        for k, options, v in initial:
            self.append(VariantValue(Variant(k, options), v))
                
    
    def reconcile(self, variant_value):
        assert isinstance(variant_value, VariantValue)
        for vv in self:
            if vv.variant.name == variant_value.variant.name:
                vv.reconcile(variant_value)
                return
        # Otherwise just append it to the end
        self.append(variant_value)

    def reconcile_all(self, variant_values):
        assert isinstance(variant_values, VariantValues)
        for vv in variant_values:
            self.reconcile(vv)

    def sub(self, variant_names):
        result = VariantValues()
        for vv in self:
            if vv.variant.name in variant_names:
                result.append(vv)
        return result
                             

    def names(self):
        return [ vv.variant.name for vv in self ]

    def __cmp(self, vv1, vv2):
        return cmp(vv1.variant.name, vv2.variant.name)

    def sort(self):
        super(VariantValues, self).sort(self.__cmp)

    def contains(self, variant_name):
        for vv in self:
            if vv.variant.name == variant_name:
                return True
        return False

    def lookup(self, variant_name):
        for vv in self:
            if vv.variant.name == variant_name:
                return vv.value
        raise pmutils.NoMatchException(variant_name)
        
    
    def path_string(self):
        return '-'.join([ vv.value for vv in self ])

    def __str__(self):
        return '[' + ','.join([ "%s=%s<%s>" % (vv.variant.name, vv.value, str(vv.variant.values)) for vv in self ]) + ']'
